package org.sauceggplant;

import javax.swing.*;
import java.io.File;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 加密
 *
 * @author jacob
 * @version 0.1
 */
public class Encrypt {

    private JProgressBar progressBar;

    public Encrypt(JProgressBar progressBar) {
        this.progressBar = progressBar;
    }

    /**
     * 加密
     *
     * @param file
     */
    public void encrypt(File file) {
        System.out.println("Encrypting file: " + file.getAbsolutePath());
        byte[] srcData = FileUtil.file2bytes(file);
        byte[][] srcDatas = ByteUtil.split(srcData, 245);
        byte[][] encryptDatas = new byte[srcDatas.length][];
        File pubKeyFile = new File("key/PublicKey.pem");
        if (!pubKeyFile.exists()) {
            JOptionPane.showMessageDialog(null, "PublicKey.pem证书文件不存在", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        byte[] publicKeyData = PemUtil.readPem(pubKeyFile);
        PublicKey publicKey = null;
        try {
            publicKey = KeyFactory.getInstance(PemUtil.ALGORITHM).generatePublic(new X509EncodedKeySpec(publicKeyData));
        } catch (InvalidKeySpecException e) {
            JOptionPane.showMessageDialog(null, "PublicKey.pem证书无效", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        } catch (NoSuchAlgorithmException e) {
            JOptionPane.showMessageDialog(null, "PublicKey.pem证书算法错误", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        final CountDownLatch countDownLatch = new CountDownLatch(srcDatas.length);
        FutureTask<byte[]>[] futureTasks = new FutureTask[srcDatas.length];
        System.out.println("Encrypting block size: " + srcDatas.length);
        progressBar.setMaximum(srcDatas.length);
        new Thread(new Progress(progressBar, srcDatas.length, countDownLatch)).start();
        for (int i = 0; i < srcDatas.length; i++) {
            futureTasks[i] = new FutureTask<byte[]>(new Task(countDownLatch, publicKey, srcDatas[i]));
            new Thread(futureTasks[i]).start();
        }
        try {
            countDownLatch.await();
            for (int i = 0; i < srcDatas.length; i++) {
                encryptDatas[i] = futureTasks[i].get();
                if (encryptDatas[i] == null) {
                    throw new InterruptedException("加密模块" + i + "返回数据为空");
                }
            }
            String path = file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - file.getName().length());
            File eFile = FileUtil.byteArray2file(encryptDatas, path + file.getName() + ".edata");
            System.out.println("Encrypted file: " + eFile.getAbsolutePath());
        } catch (InterruptedException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "加密任务被中断，加密失败", "Error", JOptionPane.ERROR_MESSAGE);
        } catch (ExecutionException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "加密失败", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    static class Task implements Callable<byte[]> {
        private CountDownLatch countDownLatch;
        private PublicKey publicKey;
        private byte[] srcData;

        public Task(CountDownLatch countDownLatch, PublicKey publicKey, byte[] srcData) {
            this.countDownLatch = countDownLatch;
            this.publicKey = publicKey;
            this.srcData = srcData;
        }

        public byte[] call() {
            byte[] data = null;
            try {
                data = RSAUtil.encrypt(publicKey, srcData);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
            return data;
        }
    }
}
